#include <iostream>
#include <sstream>
#include "RTPPackage.hpp"
#include "RTPBufferReader.hpp"
#include "MuxerToMP4.hpp"

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <thread>

extern "C"
{
//编码
#include <libavcodec/avcodec.h>
//封装格式处理
#include <libavformat/avformat.h>
//像素处理
#include <libswscale/swscale.h>

#include <libavutil/mathematics.h>
#include <libavutil/time.h>

};


#define TEST 1


using namespace std;


struct MediaData {
    AVFormatContext *pFormatCtx;
    AVStream *v_stream{NULL};
    int v_stream_idx{-1};

    AVStream *a_stream{NULL};
    int a_stream_idx{-1};

    MediaData() {
        //封装格式上下文，统领全局的结构体，保存了视频文件封装格式的相关信息
        pFormatCtx = avformat_alloc_context();
    }
};

string bytes2HexString(unsigned char *data, int len) {
    stringstream ss;
    char temp[3];
    for (int i = 0; i < len; i++) {
        sprintf(temp, "%02x", data[i]);
        ss << temp;
    }
    return ss.str();
}

int read_media_file(std::string &input, MediaData &media_data) {

    AVFormatContext *pFormatCtx = media_data.pFormatCtx;

    //2.打开输入视频文件
    if (avformat_open_input(&pFormatCtx, input.c_str(), NULL, NULL) != 0) {
        printf("%s", "无法打开输入视频文件");
        return -1;
    }

    //3.获取视频文件信息
    if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
        printf("%s", "无法获取视频文件信息");
        return -1;
    }


    //获取视频流的索引位置
    //遍历所有类型的流（音频流、视频流、字幕流），找到视频流
    int i = 0;
    //number of streams
    for (; i < pFormatCtx->nb_streams; i++) {
        //流的类型
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
            media_data.v_stream_idx = i;
            media_data.v_stream = pFormatCtx->streams[i];
        } else if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
            media_data.a_stream_idx = i;
            media_data.a_stream = pFormatCtx->streams[i];
        }
    }

    if (media_data.v_stream_idx == -1) {
        printf("%s", "找不到视频流\n");
        return -1;
    }


    //只有知道视频的编码方式，才能够根据编码方式去找到解码器
    //获取视频流中的编解码上下文
    AVCodecContext *pCodecCtx = pFormatCtx->streams[media_data.v_stream_idx]->codec;
    //4.根据编解码上下文中的编码id查找对应的解码
    AVCodec *pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
    if (pCodec == NULL) {
        printf("%s", "找不到解码器\n");
        return -1;
    }

    //5.打开解码器
    if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
        printf("%s", "解码器无法打开\n");
        return -1;
    }


    //输出视频信息
    printf("视频的文件格式：%s\n", pFormatCtx->iformat->name);
    printf("视频时长：%d\n", (pFormatCtx->duration) / 1000000);
    printf("视频的宽高：%d,%d\n", pCodecCtx->width, pCodecCtx->height);
    printf("解码器的名称：%s\n", pCodec->name);

    return 0;

}


int connect_socket(std::string ip, uint16_t port) {
    int sockfd = -1;

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sockfd) {
        perror("sock created");
        exit(-1);
    }

    struct sockaddr_in server;
    memset(&server, 0, sizeof(struct sockaddr_in));
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    server.sin_addr.s_addr = inet_addr(ip.c_str());

    int res = -1;
    res = connect(sockfd, (struct sockaddr *) &server, sizeof(server));
    if (-1 == res) {
        perror("sock connect");
        exit(-1);
    }

    return sockfd;
}


void getADTS(uint8_t adts[7], AVStream * a_stream,  AVPacket *packet){

    uint8_t * bits = adts;
    int sample_index = 0 , channel = 0;
    char temp = 0;
    int length = 7 + packet->size;
    sample_index = (a_stream->codec->extradata[0] & 0x07) << 1;
    temp = (a_stream->codec->extradata[1]&0x80);
    //std::cout << "sample_rate : " << a_stream->codec->sample_rate << std::endl;
    switch(a_stream->codec->sample_rate)
    {
        case 96000:
        {
            sample_index = 0x0;
            break;
        }
        case 88200:
        {
            sample_index = 0x1;
            break;
        }
        case 64000:
        {
            sample_index = 0x2;
            break;
        }
        case 48000:
        {
            sample_index = 0x3;
            break;
        }
        case 44100:
        {
            sample_index = 0x4;
            break;
        }
        case 32000:
        {
            sample_index = 0x5;
            break;
        }
        case 24000:
        {
            sample_index = 0x6;
            break;
        }
        case 20050:
        {
            sample_index = 0x7;
            break;
        }
        case 16000:
        {
            sample_index = 0x8;
            break;
        }
        case 12000:
        {
            sample_index = 0x9;
            break;
        }
        case 11052:
        {
            sample_index = 0xa;
            break;
        }
        case 8000:
        {
            sample_index = 0xb;
            break;
        }
        case 7350:
        {
            sample_index = 0xc;
            break;
        }
        default:
        {
            sample_index = sample_index + (temp>>7);
        }break;
    }
    channel = ((a_stream->codec->extradata[1] - temp) & 0xff) >> 3;
    bits[0] = 0xff;
    bits[1] = 0xf1;
    bits[2] = 0x40 | (sample_index<<2) | (channel>>2);
    bits[3] = ((channel&0x3)<<6) | (length >>11);
    bits[4] = (length>>3) & 0xff;
    bits[5] = ((length<<5) & 0xff) | 0x1f;
    bits[6] = 0xfc;

}


//  /home/ningjian/Videos/test.mp4 192.168.40.32 8888
int main(int argc, char *argv[]) {


       //muxer_main("18591950512_CH1_1528419329.h264","18591950512_CH1_1528419329.aac","test_f.mp4","");
      // return 0;

    if (argc != 4) {
        std::cout << "arg error !" << endl;
        return 0;
    }
    //获取输入输出文件名
    std::string input = argv[1];

    std::cout << "input: " << input << endl;

    std::string ip_addr = argv[2];

    std::cout << "ip_addr: " << ip_addr << endl;

    uint16_t port = (uint16_t) stoi(argv[3]);

    std::cout << "port: " << port << endl;

    std::cout << "Start..." << std::endl;
#ifndef TEST
    int sockfd = connect_socket(ip_addr, port);
#else
    RTPBufferReader rtpb_reader(10,"./out");
#endif

    av_register_all();

    MediaData mediaData;
    read_media_file(input, mediaData);

    //准备读取
    //AVPacket用于存储一帧一帧的压缩数据（H264）
    //缓冲区，开辟空间
    AVPacket *packet = (AVPacket *) av_malloc(sizeof(AVPacket));

    uint8_t *mem_buffer = NULL;
    size_t mem_buffer_len = 0;

    uint16_t SN = 0;
    std::list<RTPPackage *> rtp_packages;
    uint8_t SIM[6]{0x01, 0x85, 0x91, 0x95, 0x05, 0x12};
    uint8_t CH = 1;

    uint8_t dtype = F_I;

    AVBitStreamFilterContext *bsfc = av_bitstream_filter_init("h264_mp4toannexb");

    bool i_frame_is_ok = false;  //解决音画不同步的问题，只有找到第一个关键帧后才开始发送音频

    while (av_read_frame(mediaData.pFormatCtx, packet) >= 0) {
        rtp_packages.clear();
        //只要视频压缩数据（根据流的索引位置判断）
        if (packet->stream_index == mediaData.v_stream_idx) {

            av_bitstream_filter_filter(bsfc, mediaData.v_stream->codec, NULL, &(packet->data), &(packet->size), packet->data, packet->size, 0);

            //cout << bytes2HexString(mediaData.v_stream->codec->extradata, mediaData.v_stream->codec->extradata_size) << endl;
            //0000000 14
            uint8_t start_code = packet->data[4];
            //NAL_SLICE = 1 非关键帧
            //NAL_SLICE_IDR =5 关键帧
            //NAL_SPS = 7 SPS帧
            uint8_t nal_unit_type = start_code & 0b00011111;

            if (nal_unit_type == 0x05) {  //关键帧
                i_frame_is_ok = true;
                dtype = F_I;
                mem_buffer_len = packet->size + mediaData.v_stream->codec->extradata_size;
                mem_buffer = new uint8_t[mem_buffer_len];
                //SPS and PPS
                memcpy(mem_buffer, mediaData.v_stream->codec->extradata, mediaData.v_stream->codec->extradata_size);
                memcpy(mem_buffer + mediaData.v_stream->codec->extradata_size, packet->data, packet->size);
            } else {
                dtype = F_P;
                mem_buffer_len = packet->size;
                mem_buffer = new uint8_t[mem_buffer_len];
                memcpy(mem_buffer, packet->data, packet->size);
            }


            //std::cout << "nal_unit_type = " << (int) nal_unit_type << endl;

            cout << "send package:" << bytes2HexString(mem_buffer, 30) << endl;
            RTPPackage::GetRTPPackagesOnFrameData(rtp_packages,
                                                  SN,
                                                  SIM,
                                                  CH,
                                                  PT_H264,
                                                  dtype,
                                                  packet->pts,
                                                  mem_buffer_len,
                                                  mem_buffer);


        } else if (packet->stream_index == mediaData.a_stream_idx) {

            dtype = A_F;

            mem_buffer_len = packet->size + 7;
            mem_buffer = new uint8_t[mem_buffer_len];
            //ADTS
            getADTS(mem_buffer,mediaData.a_stream,packet);
            memcpy(mem_buffer +7, packet->data, packet->size);

            //std::cout << bytes2HexString(mem_buffer, 20) << endl;
            RTPPackage::GetRTPPackagesOnFrameData(rtp_packages,
                                                  SN,
                                                  SIM,
                                                  CH,
                                                  PT_AAC,
                                                  dtype,
                                                  packet->pts,
                                                  mem_buffer_len,
                                                  mem_buffer);
        }


        for (auto rtp_p : rtp_packages) {
            size_t out_len;
            //std::cout << "sn:" << rtp_p->SN << endl;
            uint8_t *p_data = rtp_p->getSendData(out_len);

            if (i_frame_is_ok) {
                #ifndef TEST
                write(sockfd, p_data, out_len);
                #else
                // 自发自收
                //std::cout << "send RTP HEAD:" << bytes2HexString(p_data,30) << endl;
                rtpb_reader.ReadBuffer(p_data, out_len);
                #endif
            }


        }

        if (packet->stream_index == mediaData.v_stream_idx) {
            std::cout << "packet duration:" << packet->duration << std::endl;
            //std::this_thread::sleep_for(std::chrono::milliseconds(packet->duration));
            std::this_thread::sleep_for(std::chrono::milliseconds(10));
        }

        delete[] mem_buffer;

    }
#ifndef TEST
    close(sockfd);
#endif

    return 0;
}